<?php

/**
 * This is the model class for table "task".
 *
 * The followings are the available columns in table 'task':
 * @property string $id
 * @property string $name
 * @property string $url
 * @property string $from
 * @property integer $time_create
 * @property integer $time_update
 * @property integer $time_begin
 * @property string $status
 * @property integer $update_duration
 * @property integer $is_continue
 * @property integer $per_max
 */
class Task extends CActiveRecord
{

    const STATUS_TYPE_INIT = 'init';
    const STATUS_TYPE_WAITING = 'waiting';
    const STATUS_TYPE_PREPARE = 'prepare';
    const STATUS_TYPE_PICKING = 'picking';
    const STATUS_TYPE_DOWNING = 'downing';
    const STATUS_TYPE_COMPLETE = 'complete';
    const STATUS_TYPE_FAILED = 'failed';
    const STATUS_TYPE_OVER = 'over';


    const TYPE_FROM_YUOTUBE = 'youtube';

    public $searchKey;
    public $time_begin;
    public $time_end;


    private static $_dls = [];
    private static $_options;

    public function onAfterSave($event)
    {
        parent::onAfterSave($event);
    }


    public function getDl()
    {

        if (empty(self::$_dls[$this->from])) {
            switch ($this->from) {
                case self::TYPE_FROM_YUOTUBE:
                    self::$_dls[$this->from] = Yii::createComponent(['class' => YoutubeDlCommand::class,]);
                    break;
            }
        }
        return self::$_dls[$this->from];

    }


    /**
     * @return string the associated database table name
     */
    public function tableName()
    {
        return 'task';
    }

    public static function getOptions($attribute, $options = array())
    {
        if (!isset(self::$_options[$attribute])) {
            $opts = [];
            switch ($attribute) {
                case 'tasks':
                    $tasks = self::model()->findAll();
                    foreach ($tasks as $task) {
                        $opts[$task->id] = $task->name;
                    }
                    break;
                case 'from_types':
                    $opts[self::TYPE_FROM_YUOTUBE] = 'youtube';
                    break;
                case 'status':
                    $opts[self::STATUS_TYPE_INIT] = '采集初始化';
                    $opts[self::STATUS_TYPE_WAITING] = '等待采集';
                    $opts[self::STATUS_TYPE_PREPARE] = '采集信息中';
                    $opts[self::STATUS_TYPE_PICKING] = '采集信息采集完成';
                    $opts[self::STATUS_TYPE_DOWNING] = '采集任务视频下载中';
                    $opts[self::STATUS_TYPE_COMPLETE] = '采集阶段性完成';
                    $opts[self::STATUS_TYPE_OVER] = '采集终结';

                case 'update_duration':
                    $opts[0] = '永不更新';
                    $opts[3600] = '一小时';
                    $opts[43200] = '12小时';
                    $opts[86400] = '24小时';
                    break;


            }
            self::$_options[$attribute] = $opts;
        }
        if (is_array($options) && !empty($options)) {
            foreach (self::$_options[$attribute] as $k => $v) {
                $options[$k] = $v;
            }
        } else {
            $options = self::$_options[$attribute];
        }
        return $options;
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules()
    {

        return array(
            array('time_create,per_max, time_update, time_begin, update_duration, is_continue', 'numerical', 'integerOnly' => true),
            array('url', 'length', 'max' => 128),
            array('name', 'length', 'max' => 64),
            array('url', 'url', 'on' => 'insert'),
            array('url', 'unique'),
            array('id, name,from,url, time_create, time_update, time_begin, status, update_duration, is_continue', 'safe', 'on' => 'search'),
            array('id, name,from,url, time_create, time_update, time_begin, status, update_duration, is_continue', 'safe',),
            array(
                'status',
                'default',
                'value' => self::STATUS_TYPE_INIT,
                'setOnEmpty' => true,
                'on' => 'insert'
            ),
            array(
                'time_update',
                'default',
                'value' => time(),
                'setOnEmpty' => false,
                'on' => 'update'
            ),
            array(
                'time_create, time_update,time_begin',
                'default',
                'value' => time(),
                'setOnEmpty' => false,
                'on' => 'insert'
            ),
            array(
                'per_max',
                'default',
                'value' => 1,
                'setOnEmpty' => true,
                'on' => 'insert'
            ),
        );
    }

    /**
     * @return array relational rules.
     */
    public function relations()
    {
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
        return array(
            'TopicExt' => array(self::HAS_MANY, 'TopicExt', ['ext_id' => 'id']),
            'Videos' => array(self::HAS_MANY, 'Video', ['task_id' => 'id']),
        );
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels()
    {
        return array(
            'id' => 'ID',
            'name' => '采集名称',
            'url' => '采集Url',
            'from' => '任务来源',
            'time_create' => 'Time Create',
            'time_update' => 'Time Update',
            'time_begin' => 'Time Begin',
            'status' => 'Status',
            'update_duration' => '任务更新时间',
            'is_continue' => 'Is Continue',
            'per_max' => '采集最新视屏数量',
        );
    }

    public function searchByKey()
    {

        $criteria = new CDbCriteria;

        if (!isset($_GET['Task_sort'])) {
            $criteria->order = implode(explode($_GET['Task_sort'], '.'), '=');
        }

        if (!empty($ts_begin = strtotime($this->time_begin))) {
            $criteria->addCondition('`time_create` >= ' . $ts_begin);
        }
        if (!empty($ts_end = strtotime($this->time_end))) {
            $criteria->addCondition('`time_create` <= ' . $ts_end);
        }

        return new CActiveDataProvider($this, array(
            'criteria' => $criteria,
        ));
    }


    /**
     * Returns the static model of the specified AR class.
     * Please note that you should have this exact method in all your CActiveRecord descendants!
     * @param string $className active record class name.
     * @return Task the static model class
     */
    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }

    public static function getVideos($tid, $page, $size)
    {
        $criteria = new CDbCriteria();
        $criteria->offset = $page;
        $criteria->limit = $size;
        $criteria->addColumnCondition(['status' => Video::STATUS_COMPLETE,]);
        $criteria->addColumnCondition(['task_id' => $tid,]);
        return Video::model()->findAll($criteria);
    }

    public function queryVideos($size, $status)
    {
        $criteria = new CDbCriteria();
        $criteria->limit = $size;
        $criteria->addColumnCondition(['status' => $status,]);
        $criteria->addColumnCondition(['task_id' => $this->id,]);
        return Video::model()->findAll($criteria);

    }

    public static function activeTasks()
    {
        $count = self::model()->updateAll(['status' => self::STATUS_TYPE_WAITING], 'status =:status', [':status' => self::STATUS_TYPE_INIT]);
        return $count > 0;
    }


    public function isInit()
    {
        return $this->status == self::STATUS_TYPE_INIT;
    }

    public function isWaiting()
    {
        return $this->status == self::STATUS_TYPE_WAITING;
    }

    public function isPicking()
    {
        return $this->status == self::STATUS_TYPE_PICKING;
    }

    public function isDownLoading()
    {
        return $this->status == self::STATUS_TYPE_DOWNING;
    }

    public function isFailed()
    {
        return $this->status == self::STATUS_TYPE_FAILED;
    }

    public function isComplete()
    {
        return $this->status == self::STATUS_TYPE_COMPLETE;
    }


    /**
     * @return bool
     */
    public function activeTask()
    {
        $this->status = self::STATUS_TYPE_WAITING;
        return $this->save();
    }


    /**
     * 下载视频信息
     */
    public function downLoadVideoInfos()
    {
        $error = 0;
        $msg = '';
        if (!$this->isWaiting()) {
            throw  new Exception($this->status . ' task status is not allowed for download videos infos');
        }
        $this->status = self::STATUS_TYPE_PREPARE;
        if (!$this->save()) {
            throw  new Exception('task status update picking failed');
        }
        /** @var YoutubeDlCommand $dl */
        $dl = $this->getDl();
        if ($dl->downLoadVideoInfos($this->url)) {
            $this->status = self::STATUS_TYPE_PICKING;
            if (!$this->save()) {
                throw  new Exception('task status update picking failed');
            }
        } else {
            $this->status = self::STATUS_TYPE_FAILED;
            $error = '1';
            $msg = 'task pick Video infos Failed';
        }
        return [$error, $msg,];
    }

    /**
     * 加载视频信息
     * @return array
     * @throws Exception
     */
    public function loadVideosInfos()
    {
        $error = 0;
        $msg = [];
        $errorMsg = [];
        if (!$this->isPicking()) {
            throw  new Exception($this->status . ' task status is not allowed for load videos infos');
        }
        /** @var YoutubeDlCommand $dl */
        $dl = $this->getDl();
        $videoInfos = $dl->getVideoInfos($this->url);
        uasort($videoInfos,function ($a,$b) {
            if ($a['playlist_index'] == $b['playlist_index']) {
                return 0;
            }
            return ($a['playlist_index'] < $b['playlist_index']) ? 1 : -1;
        });
        $per_max = $this->per_max?$this->per_max :1;
        $videoInfos = array_slice($videoInfos, 0, $per_max);
        foreach ($videoInfos as $info) {
            /** @var Video $video */
            $video = Video::build($info, $this->id);
            if ($video&&!$video->save()) {
                $errorMsg[] = $video->getErrors();
                $error = 1;
            } else if($video){
                $msg[$video->id] = $info;
            }
        }
        if (!empty($errorMsg)) {
            $msg['error_msg'] = $errorMsg;
        } else {
            $this->status = self::STATUS_TYPE_DOWNING;
            $this->save();
        }
        return [$error, $msg];
    }


    /**
     * 下载视频内容
     */
    public function downLoadVideosContent()
    {
        $error = 0;
        $msg = [];
        if (!$this->isDownLoading()) {
            throw  new Exception($this->status . ' task status is not allowed for download videos content');
        }
        try {
            $waitingVideos = $this->queryVideos(1, Video::STATUS_WAITING);
            /** @var Video $video */
            foreach ($waitingVideos as $video) {
                list ($error, $msg) = $video->downLoad($this->getDl());
                if ($error) {
                    Yii::log($msg, CLogger::LEVEL_ERROR, __METHOD__);
                }
            }
            if (!Video::model()->countByAttributes(['status' => [Video::STATUS_WAITING]])) {
                if ($this->update_duration != 0) {
                    $this->status = self::STATUS_TYPE_COMPLETE;
                }else{
                    $this->status = self::STATUS_TYPE_OVER;
                }
                $this->save();
            }
        } catch (Exception $e) {
            $error = empty($e->getCode()) ? 1 : $e->getCode();
            $msg = $e->getMessage();
        }
        return [$error, $msg];
    }

    /**
     * 刷新采集任务状态
     * @return array
     * @throws Exception
     */
    public function refreshTask()
    {
        $error = 0;
        $msg = [];
        if (!$this->isFailed() && !$this->isComplete()) {
            throw  new Exception($this->status . ' task status is not allowed for refresh');
        }

        if ($this->update_duration == 0) {
            $this->status = self::STATUS_TYPE_OVER;
            $this->save();
        }

        if ($this->update_duration > 0 && time() - $this->time_update >= $this->update_duration) {
            $this->status = self::STATUS_TYPE_WAITING;
            $this->save();
        }
        return [$error, $msg];
    }

}
